package com.example.controller;

import com.example.common.Result;
import com.example.common.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ExecutionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
* @description
* @author        庞留杰
* @date          2020/8/24 13:44
* @version       V1.0
**/
@Slf4j
@Transactional
@RequestMapping("/flowable")
@RestController
public class DemoController{
    @Autowired
    private RepositoryService repositoryService;

    // 流程运行控制服务
    @Autowired
    private RuntimeService runtimeService;
    // 任务管理服务
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    // 流程引擎
    @Autowired
    private ProcessEngine processEngine;

    // http://localhost:8888/flowable/create
    // 署工作流文件到流程引擎，其实就是将xml文件解析成Java对象，从而可以在程序里使用.
    @GetMapping("/create")
    public void create() {
        RepositoryService repositoryService = processEngine.getRepositoryService();

        //获取部署构建器

        //名称一定要是bpmn20.xml,否则发布不会成功

        Deployment deployment = repositoryService.createDeployment()
                .name("我的第一个流程")
//                .addClasspathResource("processe/OrderApproval.bpmn20.xml")
                .addClasspathResource("processe/OrderApproval.bpmn20.xml")
                .deploy();

        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();

        System.out.println("部署："+deployment);
        // 流程定义ID
        System.out.println("processDefinitionKey : " + processDefinition.getId());
        System.out.println("Found process definition : " + processDefinition.getName());


//        InputStream in = this.getClass().getClassLoader().getResourceAsStream("processe/OrderApproval.zip");
//        ZipInputStream zipInputStream = new ZipInputStream(in);
//        Deployment deployment = processEngine.getRepositoryService()//获取流程定义和部署对象相关的Service  
//                .createDeployment()//创建部署对象 
//                .addZipInputStream(zipInputStream)//使用zip方式部署，将approve.bpmn和approve.png压缩成zip格式的文件  
//                .deploy();//完成部署
//        System.out.println("部署ID："+deployment.getId());
//        System.out.println("部署时间："+deployment.getDeploymentTime());

    }

    /**
     * http://localhost:8888/flowable/start/1/1
     * 提交采购订单的审批请求
     * @param userId 用户id
     */
    @PostMapping("/start/{userId}/{purchaseOrderId}")
    public Result startFlow(@PathVariable String userId, @PathVariable String purchaseOrderId) {
        // 存放数据
        HashMap<String, Object> map = new HashMap();
        map.put("userId", userId);
        map.put("purchaseOrderId", purchaseOrderId);
        map.put("businessKey", "businessKey用于与真正的业务id关联");
        //启动流程
//        ProcessInstance processInstance =
//                runtimeService.startProcessInstanceByKey("OrderApproval", map);

        /**
         *  processDefinitionKey
         *  businessKey
         * */
        ProcessInstance processInstance =
                runtimeService.startProcessInstanceByKey("OrderApproval", map);

//        ProcessInstance processInstance =
//                runtimeService.startProcessInstanceByKey("level", map);

        String processId = processInstance.getId();
        String name = processInstance.getName();
        String businessKey = processInstance.getBusinessKey();

        System.out.println(processId + ":" + name + ":" + businessKey);
        return ResultUtil.success(processId + ":" + name);
    }

    /**
     * http://localhost:8888/flowable/getTasks/1
     * 获取用户的任务
     * @param userId 用户id
     */
    @GetMapping("/getTasks/{userId}")
    public Result getTasks(@PathVariable String userId) {
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
        return ResultUtil.success(tasks.toString());
    }

    /**
     * http://localhost:8888/flowable/success/1
     * 审批通过
     */
    @PostMapping("/success/{taskId}")
    public Result success(@PathVariable String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            return ResultUtil.error("流程不存在");
        }
        //通过审核
        HashMap<String, Object> map = new HashMap();
        map.put("approved", true);
        taskService.complete(taskId, map);
        return ResultUtil.success("流程审核通过！");
    }

    /**
     * 审批不通过
     */
    @PostMapping("/faile/{taskId}")
    public Result faile(@PathVariable String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            return ResultUtil.error("流程不存在");
        }
        //通过审核
        HashMap<String, Object> map = new HashMap();
        map.put("approved", false);
        taskService.complete(taskId, map);
        return ResultUtil.success("成功");
    }

    @GetMapping("/query")
    public void processInstanceQuery(String processDefinitionKey){
        //根据流程定义Key值查询正在运行的流程实例
        List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processDefinitionKey(processDefinitionKey).list();
       for (ProcessInstance  p:processInstanceList){
          System.out.println("p1==="+p);
       }

        //查询激活的流程实例
        List<ProcessInstance> processInstanceList1 = runtimeService.createProcessInstanceQuery().processDefinitionKey(processDefinitionKey).active().list();
        for (ProcessInstance  p:processInstanceList1){
            System.out.println("p2==="+p);
        }

        //相反 查询挂起的流程则是
        List<ProcessInstance> suspendList = runtimeService.createProcessInstanceQuery().processDefinitionKey(processDefinitionKey).suspended().list();
        for (ProcessInstance  p:suspendList){
            System.out.println("p3==="+p);
        }

        //根据变量来查询
        // 根据title='启动流程',以及processDefinitionKey来作为查询条件进行查询
        List<ProcessInstance> varList = runtimeService.createProcessInstanceQuery().variableValueEquals("title","启动流程").list();
        for (ProcessInstance  p:varList){
            System.out.println("p4==="+p);
        }
    }


    //生成当前流程图表   http://localhost:8888/flowable/processDiagram?processId=[processInstanceId]
    @Deprecated // 。。。。。。。。。 过时
    @RequestMapping(value = "processDiagram")
    public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        System.out.println("pi==="+pi);
        //流程走完的不显示图
        if (pi == null) {
            return;
        }
        // 在有并行网关的情况下，此处会报错,获取的task一个集合
        // Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        String InstanceId = processId;
        //使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
        //String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();
        //得到正在执行的Activity的Id
        List<String> activityIds = new ArrayList();
        List<String> flows = new ArrayList();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        }
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();

        //防止图片乱码
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds,
                flows,engconf.getActivityFontName(),engconf.getLabelFontName(),engconf.getAnnotationFontName(),
                engconf.getClassLoader(),1.0,true);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }


    /**  http://localhost:8888/flowable/getHistoryProcess?processId=5
     * 任务历史
     * @param processId 部署id
     */
    @RequestMapping(value = "getHistoryProcess")
    public List<HistoricActivityInstance> getHistoryProcess(String processId) {
        List<HistoricActivityInstance> list = historyService // 历史相关Service
                .createHistoricActivityInstanceQuery() // 创建历史活动实例查询
                .processInstanceId(processId) // 执行流程实例id
                .finished()
                .list();

        for (HistoricActivityInstance activity : list) {
            System.out.println(activity.getActivityId() + " took "
                    + activity.getDurationInMillis() + " milliseconds");
        }

        return list;
    }


    // 获取流程图，已经合并
    @RequestMapping(value = "/v2/processDiagram")
    public void genProcessDiagram1(HttpServletResponse httpServletResponse, String processInstanceId,String executionId) {
        log.info("流程实例ID（processInstanceId）"+processInstanceId);
        log.info("执行实例ID（executionId）"+executionId);

        /**
         * 获得当前活动的节点
         */
        String processDefinitionId = "";
        /**
         * 获得活动的节点
         *  ----------------- 会显示多实例中所有人走过
         */
        /*List<String> highLightedActivitis = new ArrayList<>();
        if (this.isFinished(processInstanceId)) {// 如果流程已经结束，则得到结束节点
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId=pi.getProcessDefinitionId();
        } else {// 如果流程没有结束，则取当前活动节点
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId=pi.getProcessDefinitionId();

            List<Execution> executions = runtimeService.createExecutionQuery()
                    .processInstanceId(processInstanceId).list();
            for (Execution exe : executions) {
                List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
                highLightedActivitis.addAll(ids);
            }
        }*/

        // 改造，多实例中只显示自己走过的
        List<String> highLightedActivitis = new ArrayList<>();
        if (this.isFinished(processInstanceId)) {// 如果流程已经结束，则得到结束节点
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId=pi.getProcessDefinitionId();
        } else {// 如果流程没有结束，则取当前活动节点
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId=pi.getProcessDefinitionId();
            ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
            executionQuery.processInstanceId(processInstanceId);
            //用于多实例
            if (StringUtils.isNotEmpty(executionId)) { //执行实例ID
                executionQuery.executionId(executionId);
            }
            List<Execution> executions = executionQuery.list();
            for (Execution exe : executions) {
                List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
                highLightedActivitis.addAll(ids);
            }
        }

        /*
            让所有走过的节点高亮
            List<HistoricActivityInstance> highLightedActivitList =  historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
            for(HistoricActivityInstance tempActivity : highLightedActivitList){
                String activityId = tempActivity.getActivityId();
                highLightedActivitis.add(activityId);
            }
        */
        List<String> flows = new ArrayList<>();
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();

        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "bmp", highLightedActivitis, flows,
                engconf.getActivityFontName(),
                engconf.getLabelFontName(),
                engconf.getAnnotationFontName(),
                engconf.getClassLoader(), 1.0, true);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } catch (IOException e) {
            log.error("操作异常",e);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 判断流程是否结束
    public boolean isFinished(String processInstanceId) {
        return historyService.createHistoricProcessInstanceQuery().finished()
                .processInstanceId(processInstanceId).count() > 0;
    }
}
